import { Logger } from '@nestjs/common';
import * as crypto from 'crypto';
import * as fs from 'fs';

const algorithm = 'aes-256-cbc';
const iv = Buffer.from('V2cryptoWillWin!');

// 加密文件
export function encryptFile(inputPath: string, outputPath: string, password: string) {
  const key = crypto.createHash('sha256').update(password).digest('base64').substring(0, 32);

  const cipher = crypto.createCipheriv(algorithm, key, iv);

  const input = fs.createReadStream(inputPath);
  const output = fs.createWriteStream(outputPath);

  output.write(iv); // 写入初始向量

  input.pipe(cipher).pipe(output);

  output.on('finish', () => {
    console.log('文件加密完成');
  });
}

// 解密文件
export function decryptFile(inputPath: string, outputPath: string, password: string) {
  const decryptedData = decryptFileToStrSimple(inputPath, password);
  fs.writeFileSync(outputPath, decryptedData);
}

// 加密文件
export function encryptStrToFileSimple(inputString: string, outputPath: string, password: string) {
  const key = crypto.createHash('sha256').update(password).digest('base64').substring(0, 32);
  const cipher = crypto.createCipheriv(algorithm, key, iv);
  const encryptedData = Buffer.concat([cipher.update(inputString, 'utf8'), cipher.final()]);
  const outputData = Buffer.concat([iv, encryptedData]);
  try {
    fs.writeFileSync(outputPath, outputData);
    console.log('加密文件保存完成');
  } catch (error) {
    console.error('写入文件时发生错误:', error);
  }
}

// 解密文件
export function decryptFileToStrSimple(inputPath: string, password: string): string {
  const key = crypto.createHash('sha256').update(password).digest('base64').substring(0, 32);
  if (!fs.existsSync(inputPath)) {
    throw new Error(`解密文件不存在:[${inputPath}]`);
  }
  const input = fs.readFileSync(inputPath);
  const iv = Buffer.alloc(16);
  const encryptedData = Buffer.alloc(input.length - 16);

  input.copy(iv, 0, 0, 16);
  input.copy(encryptedData, 0, 16);
  const decipher = crypto.createDecipheriv(algorithm, key, iv);
  const decryptedData = Buffer.concat([decipher.update(encryptedData), decipher.final()]);
  return decryptedData.toString('utf8');
}

export async function decryptFileUtilSuccess(inputPath: string, asyncPasword: () => Promise<string>, logger: Logger) {
  let hashDecrypt = false;
  let info = '';
  while (!hashDecrypt) {
    try {
      const password = await asyncPasword();
      info = decryptFileToStrSimple(inputPath, password);
    } catch (err: any) {
      if (err.code == 'ERR_OSSL_BAD_DECRYPT') {
        logger.error('密码错误');
      } else {
        logger.error(err);
      }
      continue;
    }
    hashDecrypt = true;
  }
  return info;
}

export async function encryptStrToFile(inputString: string, outputPath: string, password: string) {
  const salt = crypto.randomBytes(16);
  const derivedKey = await deriveKey(password, salt.toString('hex'), 10000); // 使用PBKDF2派生密钥，迭代次数可调整

  const iv = crypto.randomBytes(16); // 生成随机初始化向量
  const cipher = crypto.createCipheriv('aes-256-gcm', derivedKey, iv);

  const encryptedData = Buffer.concat([cipher.update(inputString, 'utf8'), cipher.final()]);
  const tag = cipher.getAuthTag();

  const outputData = Buffer.concat([salt, iv, encryptedData, tag]);

  try {
    fs.writeFileSync(outputPath, outputData);
    console.log('加密文件保存完成');
  } catch (error) {
    console.error('写入文件时发生错误:', error);
  }
}

async function deriveKey(password: string, salt: string, iterations: number): Promise<Buffer> {
  return new Promise((resolve, reject) => {
    crypto.pbkdf2(password, salt, iterations, 32, 'sha256', (err, derivedKey: Buffer) => {
      if (err) {
        reject(err);
      } else {
        resolve(derivedKey);
      }
    });
  });
}

export async function decryptFileToStr(inputPath: string, password: string): Promise<string> {
  try {
    const fileData = fs.readFileSync(inputPath);

    // 分割盐、IV和加密数据
    const salt = fileData.subarray(0, 16);
    const iv = fileData.subarray(16, 32);
    const encryptedData = fileData.subarray(32, -16);
    const tag = fileData.subarray(-16); // 获取认证标签

    const derivedKey = await deriveKey(password, salt.toString('hex'), 10000); // 使用相同的迭代次数来派生密钥

    const decipher = crypto.createDecipheriv('aes-256-gcm', derivedKey, iv);
    decipher.setAuthTag(tag); // 设置认证标签
    let decryptedData = decipher.update(encryptedData, null, 'utf8');
    decryptedData += decipher.final('utf8');

    return decryptedData;
  } catch (error) {
    throw error; // 重新抛出错误以便调用者可以处理
  }
}

export function decryptFileToStrSync(inputPath: string, password: string): string {
  try {
    const fileData = fs.readFileSync(inputPath);

    // 分割盐、IV和加密数据
    const salt = fileData.subarray(0, 16);
    const iv = fileData.subarray(16, 32);
    const encryptedData = fileData.subarray(32, -16);
    const tag = fileData.subarray(-16); // 获取认证标签

    const derivedKey = crypto.pbkdf2Sync(password, salt.toString('hex'), 10000, 32, 'sha256'); // 使用相同的迭代次数来派生密钥

    const decipher = crypto.createDecipheriv('aes-256-gcm', derivedKey, iv);
    decipher.setAuthTag(tag); // 设置认证标签
    let decryptedData = decipher.update(encryptedData, null, 'utf8');
    decryptedData += decipher.final('utf8');

    return decryptedData;
  } catch (error) {
    throw error; // 重新抛出错误以便调用者可以处理
  }
}
